package eu.irreality.dai.gameplay.world;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import eu.irreality.dai.gameplay.entities.Entity;
import eu.irreality.dai.gameplay.entities.PlayerCharacter;
import eu.irreality.dai.util.Observable;
import eu.irreality.dai.util.Observer;
import eu.irreality.dai.world.gen.CACavernousLevelGenerator;
import eu.irreality.dai.world.gen.DungeonGenerator;
import eu.irreality.dai.world.gen.strategy.monster.SimpleMonsterGenerationStrategy;
import eu.irreality.dai.world.level.Level;

public class GameWorld implements Observable
{

    private PlayerCharacter pc;

    private Level activeLevel;

    private ArrayList<Observer> observers;

    private Map<String, Dungeon> dungeons = new HashMap<String, Dungeon>();

    public GameWorld()
    {
	observers = new ArrayList<Observer>();
    }

    public void generateTestDungeon()
    {
	Dungeon d = new Dungeon();
	LevelDescriptor[] descriptors = new LevelDescriptor[10];
	for (int i = 0; i < descriptors.length; i++)
	{
	    descriptors[i] = new LevelDescriptor();
	    descriptors[i]
		    .setMonsterGenerationStrategy(new SimpleMonsterGenerationStrategy());
	    descriptors[i].setNumMonsters(10);
	    if (i > 0)
	    {
		descriptors[i].addUpstairsConnection(descriptors[i - 1]);
		descriptors[i - 1].addDownstairsConnection(descriptors[i]);
	    }
	    if (i % 2 == 0)
	    {
		DungeonGenerator g = new DungeonGenerator();
		g.setRows(25);
		g.setCols(80);
		descriptors[i].setGenerator(g);
	    } else
	    {
		CACavernousLevelGenerator g = new CACavernousLevelGenerator();
		g.setRows(25);
		g.setCols(80);
		descriptors[i].setGenerator(g);
	    }
	    d.addLevelDescriptor(descriptors[i]);
	}
	dungeons.put("TD", d);
    }

    public Dungeon getDungeon(String dungeonId)
    {
	return dungeons.get(dungeonId);
    }

    public Level getLevel(String dungeonId, int levelNumber)
    {
	return dungeons.get(dungeonId).getLevel(levelNumber);
    }

    public void update()
    {
	// TODO: Temp fix. When an entity (for example the player) leaves a
	// level,
	// we get a ConcurrentModificationException on the iterator
	// => just make a copy of the collection (clone()).
	List<Entity> entities = activeLevel.getEntities();
	LinkedList<Entity> clonedEntities = (LinkedList<Entity>) ((LinkedList<Entity>) entities)
		.clone();

	for (Entity e : clonedEntities)
	{
	    e.update(this);
	}
	notifyObservers();
    }

    public void setPlayerCharacter(PlayerCharacter pc)
    {
	this.pc = pc;
    }

    public PlayerCharacter getPlayerCharacter()
    {
	return pc;
    }

    public Level getActiveLevel()
    {
	return activeLevel;
    }

    public void setActiveLevel(Level l)
    {
	// if (activeLevel != null)
	// activeLevel.removeEntity(pc);
	activeLevel = l;
	// activeLevel.addEntity(pc);
	notifyObservers("active_level_changed");
    }

    public Thread getGameWorldThread()
    {
	return new Thread()
	{
	    public void run()
	    {
		for (;;)
		{
		    GameWorld.this.update();
		}
	    }
	};
    }

    @Override
    public void addObserver(Observer o)
    {
	observers.add(o);
    }

    @Override
    public void notifyObservers()
    {
	notifyObservers(null);
    }

    @Override
    public void notifyObservers(Object arg)
    {
	for (Observer o : observers)
	{
	    o.update(this, arg);
	}
    }

}
